
package kalkon.inputModules.xslt;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import kalkon.InputModule;
import kalkon.InputModuleException;
import kalkon.Transformer;
import kalkon.URLBuilder.URLBuilder;
import nu.xom.Attribute;
import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.Nodes;
import nu.xom.ParsingException;
import nu.xom.ValidityException;
import nu.xom.XPathContext;
import org.htmlcleaner.CleanerProperties;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.SimpleXmlSerializer;
import org.htmlcleaner.TagNode;

/**
 *
 * @author tommz
 * @author newohybat newohybat@gmail.com
 */
public class XsltInputModule implements InputModule {

    Document clubInformations = null;
    XPathContext context = null;
    List<String> clubIds = null;

    public XsltInputModule() {
        context = new XPathContext();
        context.addNamespace("c", "http://code.google.com/p/kalkon/internalClub");
        context.addNamespace("clubInfo","http://code.google.com/p/kalkon/clubInfo");

        Builder builder = new Builder();
        Document doc = null;

        // TODO: predelat do rozhrani tak, aby se konstruktor volal z factory metody

        try {
            doc = builder.build(getClass().getResourceAsStream("/kalkon/inputModules/xslt/clubInformations.xml"));
        } catch (ValidityException ex) {
            Logger.getLogger(XsltInputModule.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ParsingException ex) {
            Logger.getLogger(XsltInputModule.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(XsltInputModule.class.getName()).log(Level.SEVERE, null, ex);
        }

        clubInformations = doc;


    }

    public List<String> getClubsIDs() {
        if (clubIds == null) {
            List<String> list = new ArrayList<String>();

            Nodes nodes = clubInformations.query("//c:club/@id", context);

            for (int i = 0; i < nodes.size(); i++) {
                list.add(nodes.get(i).getValue());
            }
            clubIds = list;
        }

        return clubIds;
    }

    public Document getClubInfoByID(String id) {
        if (!clubIds.contains(id)) {
            return null;
        }

        Nodes nodes = clubInformations.query("//c:club[@id='" + id + "']", context);

        assert nodes.size() == 1;

        Element club = new Element((Element) nodes.get(0));

        return new Document(club);

    }

    public Document getClubEventsByID(String id) throws InputModuleException {
        if (!clubIds.contains(id)) {
            return null;
        }

        // find path to  transformation

        Nodes n = clubInformations.query("//clubInfo:xslt[@id='" + id + "']",context);
        assert n.size() == 1;
        
        Element xslt = (Element) n.get(0);


        String xsltPath = xslt.getValue();

        // open transformer
        Transformer t = Transformer.getTransformer(getClass().getResourceAsStream(xsltPath));
 
        // get download URL

        Attribute urlAt = xslt.getAttribute("url");
        assert urlAt != null;

        String urlString = urlAt.getValue();
        
        // time tokenizer application to url
        URLBuilder builder = new URLBuilder();
        if(builder.isTransformable(urlString)){
            urlString = builder.transform(urlString);
        }

        // open URL

        URL url = null;
        try {
            url = new URL(urlString);
        } catch (MalformedURLException ex) {
            Logger.getLogger(XsltInputModule.class.getName()).log(Level.SEVERE, null, ex);
        }

        InputStream downloadedStream = null;
        try {
            downloadedStream = url.openStream();
        } catch (IOException ex) {
            Logger.getLogger(XsltInputModule.class.getName()).log(Level.SEVERE, null, ex);
        }

        // Create downloadedStream's reader and set its encoding according to the clubInformations.xml
        String charset = xslt.getAttributeValue("charset");
        InputStreamReader downloadedStreamReader = null;
        try {
            downloadedStreamReader = new InputStreamReader(downloadedStream, charset);
        } catch (UnsupportedEncodingException ex) {
            Logger.getLogger(XsltInputModule.class.getName()).log(Level.SEVERE, null, ex);
        }


        // Transform HTML to XML
        HtmlCleaner cleaner = new HtmlCleaner();
        CleanerProperties props = cleaner.getProperties();

        props.setNamespacesAware(false);
        props.setOmitComments(true);
        props.setOmitDoctypeDeclaration(true);
        props.setOmitHtmlEnvelope(false);
        props.setUseCdataForScriptAndStyle(false);
        
        
        TagNode node = null;
        try {
            node = cleaner.clean(downloadedStreamReader);
        } catch (IOException ex) {
            Logger.getLogger(XsltInputModule.class.getName()).log(Level.SEVERE, null, ex);
        }
        SimpleXmlSerializer serializer = new SimpleXmlSerializer(cleaner.getProperties());
        ByteArrayOutputStream out = new ByteArrayOutputStream();

        try {
            serializer.writeToStream(node, out);
        } catch (IOException ex) {
            // TODO pridat popis chyby do vyjimky
            System.err.println(ex);
            throw new InputModuleException();
        }

        InputStream cleanedStream = new ByteArrayInputStream(out.toByteArray());

        // Build DOM from cleaned HTML page

        Builder parser = new Builder();

        Document page = null;
        try {
            page = parser.build(cleanedStream);
        } catch (ValidityException ex) {
            Logger.getLogger(XsltInputModule.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ParsingException ex) {
            Logger.getLogger(XsltInputModule.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(XsltInputModule.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        Document ret = t.transformToDocument(page);

        ret.getRootElement().addAttribute(new Attribute("id", id));

        return ret;

    }
}
